﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.IO;

namespace CMISBrowser4Outlook
{
    class HtmlFragment
    {
        #region Read and decode from clipboard
        static public HtmlFragment FromClipboard()
        {
            string rawClipboardText = Clipboard.GetText(TextDataFormat.Html);
            HtmlFragment h = new HtmlFragment(rawClipboardText);
            return h;
        }

        public HtmlFragment(string rawClipboardText)
        {
            // This decodes CF_HTML, which is an entirely text format using UTF-8.
            // Format of this header is described at:
            // http://msdn.microsoft.com/library/default.asp?url=/workshop/networking/clipboard/htmlclipboard.asp

            // Note the counters are byte counts in the original string, which may be Ansi. So byte counts
            // may be the same as character counts (since sizeof(char) == 1).
            // But System.String is unicode, and so byte couns are no longer the same as character counts,
            // (since sizeof(wchar) == 2). 
            int startHMTL = 0;
            int endHTML = 0;

            int startFragment = 0;
            int endFragment = 0;

            Regex r;
            Match m;

            r = new Regex("([a-zA-Z]+):(.+?)[\r\n]",
                RegexOptions.IgnoreCase | RegexOptions.Compiled);

            for (m = r.Match(rawClipboardText); m.Success; m = m.NextMatch())
            {
                string key = m.Groups[1].Value.ToLower();
                string val = m.Groups[2].Value;

                switch (key)
                {
                    // Version number of the clipboard. Starting version is 0.9. 
                    case "version":
                        m_version = val;
                        break;

                    // Byte count from the beginning of the clipboard to the start of the context, or -1 if no context
                    case "starthtml":
                        if (startHMTL != 0) throw new FormatException("StartHtml is already declared");
                        startHMTL = int.Parse(val);
                        break;

                    // Byte count from the beginning of the clipboard to the end of the context, or -1 if no context.
                    case "endhtml":
                        if (startHMTL == 0) throw new FormatException("StartHTML must be declared before endHTML");
                        endHTML = int.Parse(val);

                        m_fullText = rawClipboardText.Substring(startHMTL, endHTML - startHMTL);
                        break;

                    //  Byte count from the beginning of the clipboard to the start of the fragment.
                    case "startfragment":
                        if (startFragment != 0) throw new FormatException("StartFragment is already declared");
                        startFragment = int.Parse(val);
                        break;

                    // Byte count from the beginning of the clipboard to the end of the fragment.
                    case "endfragment":
                        if (startFragment == 0) throw new FormatException("StartFragment must be declared before EndFragment");
                        endFragment = int.Parse(val);
                        m_fragment = rawClipboardText.Substring(startFragment, endFragment - startFragment);
                        break;

                    // Optional Source URL, used for resolving relative links.
                    case "sourceurl":
                        m_source = new System.Uri(val);
                        break;
                }
            } // end for

            if (m_fullText == null && m_fragment == null)
            {
                throw new FormatException("No data specified");
            }
        }

        string m_version;
        string m_fullText;
        string m_fragment;
        System.Uri m_source;

        public string Version
        {
            get { return m_version; }
        }

        public string Context
        {
            get { return m_fullText; }
        }

        public string Fragment
        {
            get { return m_fragment; }
        }

        public System.Uri SourceUrl
        {
            get { return m_source; }
        }

        #endregion

        #region Write to Clipboard

        static string To8DigitString(int x)
        {
            return String.Format("{0,8}", x);
        }

        public static void CopyToClipboard(string htmlFragment)
        {
            CopyToClipboard(htmlFragment, null, null);
        }

        public static void CopyToClipboard(string htmlFragment, string title, Uri sourceUrl)
        {
            if (title == null) title = "From Clipboard";

            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            string header =
                   @"Format:HTML Format
                    Version:1.0
                    StartHTML:<<<<<<<1
                    EndHTML:<<<<<<<2
                    StartFragment:<<<<<<<3
                    EndFragment:<<<<<<<4
                    StartSelection:<<<<<<<3
                    EndSelection:<<<<<<<3
                   ";

            string pre =
            @"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">
            <HTML><HEAD><TITLE>" + title + @"</TITLE></HEAD><BODY><!--StartFragment-->";

            string post = @"<!--EndFragment--></BODY></HTML>";
            sb.Append(header);
            if (sourceUrl != null)
            {
                sb.AppendFormat("SourceURL:{0}", sourceUrl);
            }
            int startHTML = sb.Length;

            sb.Append(pre);
            int fragmentStart = sb.Length;

            sb.Append(htmlFragment);
            int fragmentEnd = sb.Length;

            sb.Append(post);
            int endHTML = sb.Length;

            // Backpatch offsets
            sb.Replace("<<<<<<<1", To8DigitString(startHTML));
            sb.Replace("<<<<<<<2", To8DigitString(endHTML));
            sb.Replace("<<<<<<<3", To8DigitString(fragmentStart));
            sb.Replace("<<<<<<<4", To8DigitString(fragmentEnd));


            // Finally copy to clipboard.
            string data = sb.ToString();
            Clipboard.Clear();
            Clipboard.SetText(data, TextDataFormat.Html);
        }
        #endregion
    }

}
